<!DOCTYPE html>












  


<html class="theme-next gemini use-motion" lang="zh-CN">
<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">












<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">






















<link href="/blog/lib/font-awesome/css/font-awesome.min.css?v=4.7.0" rel="stylesheet" type="text/css">

<link href="/blog/css/main.css?v=6.4.2" rel="stylesheet" type="text/css">


  <link rel="apple-touch-icon" sizes="180x180" href="/blog/images/apple-touch-icon-next.png?v=6.4.2">


  <link rel="icon" type="image/png" sizes="32x32" href="/blog/images/favicon-32x32-next.png?v=6.4.2">


  <link rel="icon" type="image/png" sizes="16x16" href="/blog/images/favicon-16x16-next.png?v=6.4.2">


  <link rel="mask-icon" href="/blog/images/logo.svg?v=6.4.2" color="#222">









<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/blog/',
    scheme: 'Gemini',
    version: '6.4.2',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: false,
    fastclick: false,
    lazyload: false,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>


  




  <meta name="description" content="深入浅出的整合了React在使用过程中一些经典的问题。整理了react各个关键版本的特性差别">
<meta name="keywords" content="React哲学">
<meta property="og:type" content="article">
<meta property="og:title" content="Thinking in React">
<meta property="og:url" content="https://xit-edu.cn/2022/06/06/blog/thinking-in-react/thinking-in-react/index.html">
<meta property="og:site_name" content="厦门工学院">
<meta property="og:description" content="深入浅出的整合了React在使用过程中一些经典的问题。整理了react各个关键版本的特性差别">
<meta property="og:locale" content="zh-CN">
<meta property="og:image" content="https://xit-edu.cn/2022/06/06/blog/thinking-in-react/thinking-in-react/react-old-render.jpg">
<meta property="og:image" content="https://xit-edu.cn/2022/06/06/blog/thinking-in-react/thinking-in-react/react-fiber-render.jpg">
<meta property="og:image" content="https://xit-edu.cn/2022/06/06/blog/thinking-in-react/thinking-in-react/fiber-work-phase.jpg">
<meta property="og:image" content="https://xit-edu.cn/2022/06/06/blog/thinking-in-react/thinking-in-react/lifecycle-v15.6.jpg">
<meta property="og:image" content="https://xit-edu.cn/2022/06/06/blog/thinking-in-react/thinking-in-react/lifecycle-v15.6.png">
<meta property="og:image" content="https://xit-edu.cn/2022/06/06/blog/thinking-in-react/thinking-in-react/lifecycle-v16.3.jpg">
<meta property="og:image" content="https://xit-edu.cn/2022/06/06/blog/thinking-in-react/thinking-in-react/lifecycle-v16.4.png">
<meta property="og:updated_time" content="2022-09-06T07:53:58.084Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Thinking in React">
<meta name="twitter:description" content="深入浅出的整合了React在使用过程中一些经典的问题。整理了react各个关键版本的特性差别">
<meta name="twitter:image" content="https://xit-edu.cn/2022/06/06/blog/thinking-in-react/thinking-in-react/react-old-render.jpg">






  <link rel="canonical" href="https://xit-edu.cn/2022/06/06/blog/thinking-in-react/thinking-in-react/">



<script type="text/javascript" id="page.configurations">
  CONFIG.page = {
    sidebar: "",
  };
</script>

  <title>Thinking in React | 厦门工学院</title>
  






  <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?d3f181d3d99fc3c240567d5b682eebd1";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>






  <noscript>
  <style type="text/css">
    .use-motion .motion-element,
    .use-motion .brand,
    .use-motion .menu-item,
    .sidebar-inner,
    .use-motion .post-block,
    .use-motion .pagination,
    .use-motion .comments,
    .use-motion .post-header,
    .use-motion .post-body,
    .use-motion .collection-title { opacity: initial; }

    .use-motion .logo,
    .use-motion .site-title,
    .use-motion .site-subtitle {
      opacity: initial;
      top: initial;
    }

    .use-motion {
      .logo-line-before i { left: initial; }
      .logo-line-after i { right: initial; }
    }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-CN">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/blog/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">厦门工学院</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
    
      
        <p class="site-subtitle">软件研发工作室</p>
      
    
  </div>

  <div class="site-nav-toggle">
    <button aria-label="切换导航栏">
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>



<nav class="site-nav">
  
    <ul id="menu" class="menu">
      
        
        
        
          
          <li class="menu-item menu-item-home">
    <a href="/blog/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-home"></i> <br>首页</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-archives">
    <a href="/blog/archives/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-archive"></i> <br>归档</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-tags">
    <a href="/blog/tags/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-tags"></i> <br>标签</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-categories">
    <a href="/blog/categories/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-th"></i> <br>分类</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-works1">
    <a href="/blog/works1/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-sitemap"></i> <br>H5小游戏1</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-works2">
    <a href="/blog/works2/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-calendar"></i> <br>H5小游戏2</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-fiction">
    <a href="/blog/fiction/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-heartbeat"></i> <br>大将军归来</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-about">
    <a href="/blog/about/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-user"></i> <br>关于</a>
  </li>

      
      
    </ul>
  

  

  
</nav>



  



</div>
    </header>

    


    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          
            

          
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://xit-edu.cn/blog/2022/06/06/blog/thinking-in-react/thinking-in-react/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="对美好事物的向往，心中要充满强烈的愿望！">
      <meta itemprop="description" content="沉淀下来的知识，才是真正的财富！">
      <meta itemprop="image" content="/blog/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="厦门工学院">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">Thinking in React
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">

            
            
            

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              

              
                
              

              <time title="创建时间：2022-06-06 16:55:00" itemprop="dateCreated datePublished" datetime="2022-06-06T16:55:00+08:00">2022-06-06</time>
            

            
              

              
                
                <span class="post-meta-divider">|</span>
                

                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                
                  <span class="post-meta-item-text">更新于</span>
                
                <time title="修改时间：2022-09-06 15:53:58" itemprop="dateModified" datetime="2022-09-06T15:53:58+08:00">2022-09-06</time>
              
            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/blog/categories/WEB前端/" itemprop="url" rel="index"><span itemprop="name">WEB前端</span></a></span>

                
                
                  ，
                
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/blog/categories/WEB前端/最佳实践/" itemprop="url" rel="index"><span itemprop="name">最佳实践</span></a></span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <p>深入浅出的整合了React在使用过程中一些经典的问题。整理了react各个关键版本的特性差别</p>
<a id="more"></a>
<p>React 是一个用于构建用户界面的 JavaScript 库。React主要用于构建UI，很多人认为 React 是 MVC 中的 V（视图）。<br>React特点有：<br>1.声明式设计 −React采用声明范式，可以轻松描述应用。<br>2.高效 −React通过对DOM的模拟，最大限度地减少与DOM的交互。<br>3.灵活 −React可以与已知的库或框架很好地配合。<br>4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ，但我们建议使用它。<br>5.组件 − 通过 React 构建组件，使得代码更加容易得到复用，能够很好的应用在大项目的开发中。<br>6.单向响应的数据流 − React 实现了单向响应的数据流，从而减少了重复代码，这也是它为什么比传统数据绑定更简单。</p>
<p>MVC：model，view，controller。controller控制model如何在view显示。model和view对controller的数据流向都是单向的，从controller到model和view。<br>MVVM，model，view，ViewModel。ViewModel双向View，model通过ajax等方式修改viewModel。弱化了controller的存在。<br>双向绑定<br>正向。通过模板把数据放到模板中freemarker。<br>反向。view的改变反应到data中。<br>绑定。自动处理。Object.defineProperty。</p>
<h2 id="生命周期"><a href="#生命周期" class="headerlink" title="生命周期"></a>生命周期</h2><h3 id="要掌握生命周期首先要理解Fiber"><a href="#要掌握生命周期首先要理解Fiber" class="headerlink" title="要掌握生命周期首先要理解Fiber"></a>要掌握生命周期首先要理解Fiber</h3><p>React Fiber是个什么东西呢？官方的一句话解释是“React Fiber是对核心算法的一次重新实现”。这么说似乎太虚无缥缈，所以还是要详细说一下。</p>
<p>首先，不用太紧张，不要以为React Fiber的到来是一场大革命，实际上，对我们只是把React当做工具的开发者来说，很可能感觉不到有什么功能变化。等到React v16发布的时候，我们修改package.json中的react版本号，重新npm install，一切就搞定了，然后我们就感觉到网页性能更高了一些，如此而已。</p>
<p>当然，上面说的是“理想情况”，React Fiber对算法的修改，还是会带来一些功能逻辑的变化，后面会说。</p>
<p>为什么Facebook要搞React Fiber呢？我们先要了解现在React（也就是直到目前最新的v15版本）的局限。</p>
<h4 id="同步更新过程的局限"><a href="#同步更新过程的局限" class="headerlink" title="同步更新过程的局限"></a>同步更新过程的局限</h4><p>在现有React 15以及以前的版本中，更新过程是同步的，这可能会导致性能问题。</p>
<p>当React决定要加载或者更新组件树时，会做很多事，比如调用各个组件的生命周期函数，计算和比对Virtual DOM，最后更新DOM树，这整个过程是同步进行的，也就是说只要一个加载或者更新过程开始，那React就以不破楼兰终不还的气概，一鼓作气运行到底，中途绝不停歇。</p>
<p>表面上看，这样的设计也是挺合理的，因为更新过程不会有任何I/O操作嘛，完全是CPU计算，所以无需异步操作，的确只要一路狂奔就行了，但是，当组件树比较庞大的时候，问题就来了。</p>
<p>假如更新一个组件需要1毫秒，如果有200个组件要更新，那就需要200毫秒，在这200毫秒的更新过程中，浏览器那个唯一的主线程都在专心运行更新操作，无暇去做任何其他的事情。想象一下，在这200毫秒内，用户往一个input元素中输入点什么，敲击键盘也不会获得响应，因为渲染输入按键结果也是浏览器主线程的工作，但是浏览器主线程被React占着呢，抽不出空，最后的结果就是用户敲了按键看不到反应，等React更新过程结束之后，咔咔咔那些按键一下子出现在input元素里了。</p>
<p>这就是所谓的界面卡顿，很不好的用户体验。</p>
<p>现有的React版本，当组件树很大的时候就会出现这种问题，因为更新过程是同步地一层组件套一层组件，逐渐深入的过程，在更新完所有组件之前不停止，函数的调用栈就像下图这样，调用得很深，而且很长时间不会返回。<br><img src="react-old-render.jpg"><br>因为JavaScript单线程的特点，每个同步任务不能耗时太长，不然就会让程序不会对其他输入作出相应，React的更新过程就是犯了这个禁忌，而React Fiber就是要改变现状。</p>
<h4 id="React-Fiber的方式"><a href="#React-Fiber的方式" class="headerlink" title="React Fiber的方式"></a>React Fiber的方式</h4><p>破解JavaScript中同步操作时间过长的方法其实很简单——分片。</p>
<p>把一个耗时长的任务分成很多小片，每一个小片的运行时间很短，虽然总时间依然很长，但是在每个小片执行完之后，都给其他任务一个执行的机会，这样唯一的线程就不会被独占，其他任务依然有运行的机会。</p>
<p>React Fiber把更新过程碎片化，执行过程如下面的图所示，每执行完一段更新过程，就把控制权交还给React负责任务协调的模块，看看有没有其他紧急任务要做，如果没有就继续去更新，如果有紧急任务，那就去做紧急任务。</p>
<p>维护每一个分片的数据结构，就是Fiber。</p>
<p>有了分片之后，更新过程的调用栈如下图所示，中间每一个波谷代表深入某个分片的执行过程，每个波峰就是一个分片执行结束交还控制权的时机。<br><img src="react-fiber-render.jpg"></p>
<h4 id="为什么叫Fiber呢？"><a href="#为什么叫Fiber呢？" class="headerlink" title="为什么叫Fiber呢？"></a>为什么叫Fiber呢？</h4><p>大家应该都清楚进程（Process）和线程（Thread）的概念，在计算机科学中还有一个概念叫做Fiber，英文含义就是“纤维”，意指比Thread更细的线，也就是比线程(Thread)控制得更精密的并发处理机制。</p>
<p>上面说的Fiber和React Fiber不是相同的概念，但是，我相信，React团队把这个功能命名为Fiber，含义也是更加紧密的处理机制，比Thread更细。</p>
<h4 id="React-Fiber对现有代码的影响"><a href="#React-Fiber对现有代码的影响" class="headerlink" title="React Fiber对现有代码的影响"></a>React Fiber对现有代码的影响</h4><p>理想情况下，React Fiber应该完全不影响现有代码，但可惜并完全是这样。</p>
<p>在React Fiber中，一次更新过程会分成多个分片完成，所以完全有可能一个更新任务还没有完成，就被另一个更高优先级的更新过程打断，这时候，优先级高的更新任务会优先处理完，而低优先级更新任务所做的工作则会完全作废，然后等待机会重头再来。</p>
<p>因为一个更新过程可能被打断，所以React Fiber一个更新过程被分为两个阶段(Phase)：第一个阶段Reconciliation Phase和第二阶段Commit Phase。</p>
<p>在第一阶段Reconciliation Phase，React Fiber会找出需要更新哪些DOM，这个阶段是可以被打断的；但是到了第二阶段Commit Phase，那就一鼓作气把DOM更新完，绝不会被打断。</p>
<p>这两个阶段大部分工作都是React Fiber做，和我们相关的也就是生命周期函数。</p>
<p>以render函数为界，第一阶段可能会调用下面这些生命周期函数，说是“可能会调用”是因为不同生命周期调用的函数不同。</p>
<ul>
<li>componentWillMount</li>
<li>componentWillReceiveProps</li>
<li>shouldComponentUpdate</li>
<li>componentWillUpdate</li>
</ul>
<p>下面这些生命周期函数则会在第二阶段调用。</p>
<ul>
<li>componentDidMount</li>
<li>componentDidUpdate</li>
<li>componentWillUnmount</li>
</ul>
<p><img src="fiber-work-phase.jpg"><br>因为第一阶段的过程会被打断而且“重头再来”，就会造成意想不到的情况。</p>
<p>比如说，一个低优先级的任务A正在执行，已经调用了某个组件的componentWillUpdate函数，接下来发现自己的时间分片已经用完了，于是冒出水面，看看有没有紧急任务，哎呀，真的有一个紧急任务B，接下来React Fiber就会去执行这个紧急任务B，任务A虽然进行了一半，但是没办法，只能完全放弃，等到任务B全搞定之后，任务A重头来一遍，注意，是重头来一遍，不是从刚才中段的部分开始，也就是说，componentWillUpdate函数会被再调用一次。</p>
<p>在现有的React15中，每个生命周期函数在一个加载或者更新过程中绝对只会被调用一次；在React Fiber中，不再是这样了，第一阶段中的生命周期函数在一次加载和更新过程中可能会被多次调用！</p>
<p>使用React Fiber之后，一定要检查一下第一阶段相关的这些生命周期函数，看看有没有逻辑是假设在一个更新过程中只调用一次，有的话就要改了。</p>
<p>我们挨个看一看这些可能被重复调用的函数。</p>
<p>shouldComponentUpdate，这函数的作用就是返回一个true或者false，不应该有任何副作用，多调用几次也无妨，通过！</p>
<p>render，应该是纯函数，多调用几次无妨，通过！</p>
<p>componentWillMount、componentWillUpdate、componentWillReceiveProps，这几个函数其实是有问题的。我们从fiber的实现原理来看，这三个方法都有可能会打断fiber的收集过程。一旦这三个方法里面写了一些什么ajax请求或者一些什么副作用的操作，就变得很不可控，因为你根本就不知道fiber会被打断几次，这几个函数会被调用几次。</p>
<p>我突然想起一些初学者在chrome里面不断调试<code>componentWillReceiveProps</code>，并判断里面的状态。这就是一个非常经典的反面案例：在willReceive里面加了大量的判断逻辑，很多判断逻辑千奇百怪根本看不懂，就算你看懂了也不知道为什么要这样加。而且最关键的是你根本不敢删除或修改里面的逻辑，因为一旦修改了，程序可能就崩了，或者没有按照预期执行。</p>
<p>还有willMount和WillUpdate也是一样的问题，他们不安全都是因为这里面如果有副作用操作，会打断fiber，导致fiber重新执行，而引起了一些不可预知无法控制的结果。</p>
<p>所以这也是为什么React 16之后开始对这三个函数deprecate，在前面加上了<code>UNSAFE_</code>前缀，并且在17版本将完全删除的原因。</p>
<h3 id="旧版的生命周期（V15-6及以前）"><a href="#旧版的生命周期（V15-6及以前）" class="headerlink" title="旧版的生命周期（V15.6及以前）"></a>旧版的生命周期（V15.6及以前）</h3><p>react旧版生命周期包含三个过程：<br><img src="lifecycle-v15.6.jpg"><br>1、挂载过程<br>getDefaultProps()//初始化数据，设置默认props。<br>getInitialState()//初始化数据，设置默认state。也可以在constructor里面初始化数据。<br>componentWillMount()//组件已经完成初始化数据，即将挂载但是还未挂载，在render之前。<br>render()//页面渲染执行的逻辑，render函数把jsx编译为函数并生成虚拟dom<br>componentDidMount()//组件完成挂载，生成dom节点。</p>
<p>2、更新过程<br>componentWillReceiveProps(nextProps)//父组件props改变的时候触发。<br>shouldComponentUpdate(nextProps,nextState)//在componentWillReceiveProps之后触发，或者在setState以后，state发生变化，也会触发。在这个生命周期中return false可以阻止组件的更新，可以用于性能优化。<br>componentWillUpdate(nextProps,nextState)//即将开始更新。<br>render()//通过diff算法比较更新前后的新旧DOM树，并渲染更改后的节点。<br>componentDidUpdate(prevProps,prevState)//完成更新。</p>
<p>3、卸载过程<br>componentWillUnmount()//在移除之前调用。</p>
<p>React v16.0刚推出的时候，是增加了一个componentDidCatch生命周期函数，这只是一个增量式修改，完全不影响原有生命周期函数；但是，到了React v16.3，大改动来了，引入了两个新的生命周期函数：</p>
<ul>
<li>getDerivedStateFromProps</li>
<li>getSnapshotBeforeUpdate。</li>
</ul>
<p>这次新的API getDerivedStateFromProps实际上就是用来取代以前的函数componentWillReceiveProps。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ExampleComponent</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  state = &#123;</span><br><span class="line">    isScrollingDown: <span class="literal">false</span>,</span><br><span class="line">    lastRow: <span class="literal">null</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">static</span> getDerivedStateFromProps(nextProps, prevState) &#123;</span><br><span class="line">    <span class="keyword">if</span> (nextProps.currentRow !== prevState.lastRow) &#123;</span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            isScrollingDown:</span><br><span class="line">            nextProps.currentRow &gt; prevState.lastRow,</span><br><span class="line">            lastRow: nextProps.currentRow</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ScrollingList</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(props) &#123;</span><br><span class="line">    <span class="keyword">super</span>(props);</span><br><span class="line">    <span class="keyword">this</span>.listRef = React.createRef();</span><br><span class="line">  &#125;</span><br><span class="line">  getSnapshotBeforeUpdate(prevProps, prevState) &#123;</span><br><span class="line">    <span class="comment">//我们是否要添加新的 items 到列表?</span></span><br><span class="line">    <span class="comment">// 捕捉滚动位置，以便我们可以稍后调整滚动.</span></span><br><span class="line">    <span class="keyword">if</span> (prevProps.list.length &lt; <span class="keyword">this</span>.props.list.length) &#123;</span><br><span class="line">      <span class="keyword">const</span> list = <span class="keyword">this</span>.listRef.current;</span><br><span class="line">      <span class="keyword">return</span> list.scrollHeight - list.scrollTop;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  componentDidUpdate(prevProps, prevState, snapshot) &#123;</span><br><span class="line">    <span class="comment">//如果我们有snapshot值, 我们已经添加了 新的items.</span></span><br><span class="line">    <span class="comment">// 调整滚动以至于这些新的items 不会将旧items推出视图。</span></span><br><span class="line">    <span class="comment">// (这边的snapshot是 getSnapshotBeforeUpdate方法的返回值)</span></span><br><span class="line">    <span class="keyword">if</span> (snapshot !== <span class="literal">null</span>) &#123;</span><br><span class="line">      <span class="keyword">const</span> list = <span class="keyword">this</span>.listRef.current;</span><br><span class="line">      list.scrollTop = list.scrollHeight - snapshot;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;div ref=&#123;<span class="keyword">this</span>.listRef&#125;&gt;&#123;<span class="comment">/* ...contents... */</span>&#125;&lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">    );</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>
<p>getDerivedStateFromProps 的存在只有一个目的：让组件在 props 变化时更新 state。可以完美的解决willreceive的问题。<br>先来看React v16.3之前的生命周期函数（图中实际上少了componentDidCatch)，如下图。</p>
<p><img src="lifecycle-v15.6.png"></p>
<p>这个生命周期函数非常的对称，有componentWilUpdate对应componentDidUpdate，有componentWillMount对应componentDidMount；也考虑到了因为父组件引发渲染可能要根据props更新state的需要，所以有componentWillReceiveProps。</p>
<p>但是，这个生命周期函数的组合在Fiber之后就显得不合适了，因为，如果要开启async rendering，在render函数之前的所有函数，都有可能被执行多次。长期以来，原有的生命周期函数总是会诱惑开发者在render之前的生命周期函数做一些动作，现在这些动作还放在这些函数中的话，有可能会被调用多次，这肯定不是你想要的结果。</p>
<p>总有开发者问我，为什么不在componentWillMount里写AJAX获取数据的功能，他们的观点是，componentWillMount在render之前执行，早一点执行早得到结果。要知道，在componentWillMount里发起AJAX，不管多快得到结果也赶不上首次render，这样的IO操作放在componentDidMount里更合适。在Fiber启用async render之后，更没有理由在componentWillMount里做AJAX，因为componentWillMount可能会被调用多次，谁也不会希望无谓地多次调用AJAX吧。</p>
<p>道理说了都明白，但是历史经验告诉我们，不管多么地苦口婆心教导开发者不要做什么不要做什么，都不如直接让他们干脆没办法做。</p>
<p>随着getDerivedStateFromProps的推出，同时deprecate了一组生命周期API，包括：</p>
<ul>
<li>componentWillReceiveProps</li>
<li>componentWillMount</li>
<li>componentWillUpdate</li>
</ul>
<p>可以看到，除了shouldComponentUpdate之外，render之前的所有生命周期函数全灭，就因为太多错用滥用这些生命周期函数的做法，与其追求对称的美学，不如来点实际的，让程序员断了在这些生命周期函数里做些不该做事情的念想。</p>
<p>至于shouldComponentUpdate，如果谁还想着在里面做AJAX操作，那真的是没救了。（我曾经看到一个同事在render里面setState，我也是服了。）</p>
<p>按照官方说法，以前需要利用被deprecate的所有生命周期函数才能实现的功能，都可以通过getDerivedStateFromProps的帮助来实现。</p>
<p>这个getDerivedStateFromProps是一个静态函数，所以函数体内不能访问this，简单说，就是应该一个纯函数，纯函数是一个好东西啊，输出完全由输入决定。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> getDerivedStateFromProps(nextProps, prevState) &#123;</span><br><span class="line">  <span class="comment">//根据nextProps和prevState计算出预期的状态改变，返回结果会被送给setState</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>看到这样的函数声明，应该感受到React的潜台词：老实做一个运算就行，别在这里搞什么别的动作。</p>
<p>每当父组件引发当前组件的渲染过程时，getDerivedStateFromProps会被调用，这样我们有一个机会可以根据新的props和之前的state来调整新的state，如果放在三个被deprecate生命周期函数中实现比较纯，没有副作用的话，基本上搬到getDerivedStateFromProps里就行了；如果不幸做了类似AJAX之类的操作，首先要反省为什么自己当初这么做，然后搬到componentDidMount或者componentDidUpdate里面去。</p>
<p>所有被deprecate的生命周期函数，目前还凑合着用，但是只要用了，开发模式下会有红色警告，在下一个大版本（也就是React v17)更新时会彻底废弃。</p>
<p>React v16.3还引入了一个新的声明周期函数getSnapshotBeforeUpdate，这函数会在render之后执行，而执行之时DOM元素还没有被更新，给了一个机会去获取DOM信息，计算得到一个snapshot，这个snapshot会作为componentDidUpdate的第三个参数传入。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">getSnapshotBeforeUpdate(prevProps, prevState) &#123;</span><br><span class="line">   <span class="built_in">console</span>.log(<span class="string">'#enter getSnapshotBeforeUpdate'</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="string">'foo'</span>;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> componentDidUpdate(prevProps, prevState, snapshot) &#123;</span><br><span class="line">   <span class="built_in">console</span>.log(<span class="string">'#enter componentDidUpdate snapshot = '</span>, snapshot);</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<p>面这段代码可以看出来这个snapshot怎么个用法，snapshot咋看还以为是组件级别的某个“快照”，其实可以是任何值，到底怎么用完全看开发者自己，getSnapshotBeforeUpdate把snapshot返回，然后DOM改变，然后snapshot传递给componentDidUpdate。</p>
<p>官方给了一个例子，用getSnapshotBeforeUpdate来处理scroll，坦白说，我也想不出其他更常用更好懂的需要getSnapshotBeforeUpdate的例子，这个函数应该大部分开发者都用不上（听得懂我的潜台词吗：不要用！）</p>
<p>所以，React v16.3之后的生命周期函数一览图成了这样。</p>
<h3 id="新版的生命周期（V16-3）"><a href="#新版的生命周期（V16-3）" class="headerlink" title="新版的生命周期（V16.3）"></a>新版的生命周期（V16.3）</h3><p>React v16.3虽然是一个小版本升级，但是却对React组件生命周期函数有巨大变化。</p>
<p><img src="lifecycle-v16.3.jpg"></p>
<p>可以注意到，说getDerivedStateFromProps取代componentWillReceiveProps是不准确的，前者执行的范围应该比后者更广。因为componentWillReceiveProps只在Updating过程中才被调用，而且只在因为父组件引发的Updating过程中才被调用；而getDerivedStateFromProps在Updating和Mounting过程中都会被调用。</p>
<p>此外，从上面这个也看得出来，同样是Updating过程，如果是因为自身setState引发或者forceUpdate引发，而不是不由父组件引发，那么getDerivedStateFromProps也不会被调用。</p>
<p>这其实容易引发一些问题，不用仔细想，props改变会触发getDerivedStateFromProps，而setState不会触发getDerivedStateFromProps，这就埋下了一个地雷。从几个方面来说：一个是getDerivedStateFromProps本来就是要替代willMount、willUpdate和willReceive那些有副作用操作的，结果竟然会有些操作没办法触发；另外一个是你getDerivedStateFromProps有的能触发，有的没有触发，开发人员写代码要非常细心，一个不小心可能逻辑上就会出现问题。</p>
<p>还好，React很快意识到这个问题，在React v16.4中改正了这一点，改正的结果，就是让getDerivedStateFromProps无论是Mounting还是Updating，也无论是因为什么引起的Updating，全部都会被调用。</p>
<p>所以，上面的生命周期函数一览图要改一改。</p>
<h3 id="新版的生命周期（V16-4）"><a href="#新版的生命周期（V16-4）" class="headerlink" title="新版的生命周期（V16.4）"></a>新版的生命周期（V16.4）</h3><p><img src="lifecycle-v16.4.png"><br>用一个静态函数getDerivedStateFromProps来取代被deprecate的几个生命周期函数，就是强制开发者在render之前只做无副作用的操作，而且能做的操作局限在根据props和state决定新的state，而已。</p>
<p>这是进一步施加约束，防止开发者乱来，我说过，施加约束的哲学指导思想，是我最爱React的原因。</p>
<p>以上内容参考知乎程墨Morgan</p>
<h2 id="性能优化"><a href="#性能优化" class="headerlink" title="性能优化"></a>性能优化</h2><h3 id="虚拟化长列表"><a href="#虚拟化长列表" class="headerlink" title="虚拟化长列表"></a>虚拟化长列表</h3><p>react使用react-virtualized优化虚拟化长列表</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">'react-virtualized/styles.css'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123;Column, Table&#125; <span class="keyword">from</span> <span class="string">'react-virtualized'</span>;</span><br><span class="line"><span class="keyword">import</span> AutoSizer <span class="keyword">from</span> <span class="string">'react-virtualized/dist/commonjs/AutoSizer'</span>;</span><br><span class="line"><span class="keyword">import</span> List <span class="keyword">from</span> <span class="string">'react-virtualized/dist/commonjs/List'</span>;</span><br><span class="line">&lt;List &#123;...listProps&#125; sortBy=&#123;sortBy&#125; /&gt;</span><br></pre></td></tr></table></figure>
<p>Grid：用于优化构建任意网状的结构，传入一个二维的数组，渲染出类似棋盘的结构。<br>List：List是基于Grid来实现的，但是是一个维的列表，而不是网状。<br>Table：Table也是基于Grid来实现，表格具有固定的头部，并且可以在垂直方向上滚动<br>Masonry：同样可以在水平方向，也可以在垂直方向滚动，不同于Grid的是可以自定义每个元素的大小，或者子元素的大小也可以是动态变化的<br>Collection：类似于瀑布流的形式，同样可以水平和垂直方向滚动。<br>值得注意的是这些基础组件都是继承于React中的PureComponent，因此当state变化的时候，只会做一个浅比较来确定重新渲染与否。<br>除了这几个基础组件外，react-virtualized还提供了几个高阶组件，比如ArrowKeyStepper、AutoSizer、CellMeasurer、InfiniteLoader等。</p>
<p>AutoSizer：使用于一个子元素的情况，通过AutoSizer包含的子元素会根据父元素Resize的变化，自动调节该子元素的可视区的宽度和高度，同时调节的还有该子元素可视区真实渲染的dom元素的数目。可以动态的适应父元素宽度和高度的变化。但是也存在一个问题：子元素太长，换行后改变了子元素的高度后无法子适应，也就是说仅仅通过基础的组件List是不支持子元素的高度动态改变的场景。<br>CellMeasurer：这个高阶组件可以动态的改变子元素的高度，适用于提前不知道长列表中每一个子元素高度的情况。子列表元素的高度可以动态变化，通过CellMeasurer可以实现子元素的动态高度。<br>InfiniteLoader：这个高阶组件用于Table或者List的无限滚动，适用于滚动时异步请求等情况。看起来跟基础组件List一样，其实唯一的区别就是会在滚动的时候自动执行loadMoreRows函数去更新list</p>
<p>具体原理：用数组保存所有列表元素的位置，只渲染可视区内的列表元素，当可视区滚动时，根据滚动的offset大小以及所有列表元素的位置，计算在可视区应该渲染哪些元素。<br>具体步骤：</p>
<ol>
<li>首先确定长列表所在父元素的大小，父元素的大小决定了可视区的宽和高</li>
<li>确定长列表每一个列表元素的宽和高，同时初始的条件下计算好长列表每一个元素相对于父元素的位置，并用一个数组来保存所有列表元素的位置信息</li>
<li>首次渲染时，只展示相对于父元素可视区内的子列表元素，在滚动时，根据父元素的滚动的offset重新计算应该在可视区内的子列表元素。这样保证了无论如何滚动，真实渲染出的dom节点只有可视区内的列表元素。</li>
<li>假设可视区内能展示5个子列表元素，及时长列表总共有1000个元素，但是每时每刻，真实渲染出来的dom节点只有5个。</li>
<li>补充说明，这种情况下，父元素一般使用position：relative，子元素的定位一般使用：position：absolute或sticky<br>比如总共1000个，可视区域每次只展示10个，每个高度是5px，每屏就是50px，根据scroll的offset计算展示第几个。</li>
</ol>
<h3 id="使用shouldComponentUpdate避免调停"><a href="#使用shouldComponentUpdate避免调停" class="headerlink" title="使用shouldComponentUpdate避免调停"></a>使用shouldComponentUpdate避免调停</h3><p>当一个组件的 props 或 state 变更，React 会将最新返回的元素与之前渲染的元素进行对比，以此决定是否有必要更新真实的 DOM。当它们不相同时，React 会更新该 DOM。（diff算法）<br>即使 React 只更新改变了的 DOM 节点，重新渲染仍然花费了一些时间。在大部分情况下它并不是问题，不过如果它已经慢到让人注意了，你可以通过覆盖生命周期方法 shouldComponentUpdate 来进行提速。该方法会在重新渲染前被触发。其默认实现总是返回 true，让 React 执行更新：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">shouldComponentUpdate(nextProps, nextState) &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如果你知道在什么情况下你的组件不需要更新，你可以在 shouldComponentUpdate 中返回 false 来跳过整个渲染过程。其包括该组件的 render 调用以及之后的操作。</p>
<p>在大部分情况下，你可以继承 React.PureComponent 以代替手写 shouldComponentUpdate()。它用当前与之前 props 和 state 的浅比较覆写了 shouldComponentUpdate() 的实现。<br>当组件更新时，如果组件的 props 和 state 都没发生改变， render 方法就不会触发，省去 Virtual DOM 的生成和比对过程，达到提升性能的目的。具体就是 React 自动帮我们做了一层浅比较。否则如果无论setState有没有改变都会更新的话就会造成性能浪费。<br>例如像这种情况，PureComponent是不会更新的</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">constructor</span>(props) &#123;</span><br><span class="line">  <span class="keyword">this</span>.state = &#123;</span><br><span class="line">    words: [<span class="string">'marklar'</span>],</span><br><span class="line">    text:&#123;<span class="attr">a</span>:&#123;<span class="attr">k1</span>:<span class="number">1</span>&#125;&#125;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//这种情况下不会重新render，PureComponent和showComponentUpdate都不会更新。</span></span><br><span class="line"><span class="keyword">const</span> words = <span class="keyword">this</span>.state.words;</span><br><span class="line">words.push(<span class="string">'marklar'</span>);<span class="comment">//数组的push方法返回的并不是一个新数组</span></span><br><span class="line"><span class="keyword">this</span>.setState(&#123;<span class="attr">words</span>: words&#125;);</span><br><span class="line"><span class="comment">//举另外一个栗子</span></span><br><span class="line"><span class="keyword">let</span> foo = &#123;<span class="attr">a</span>: &#123;<span class="attr">b</span>: <span class="number">1</span>&#125;&#125;;</span><br><span class="line"><span class="keyword">let</span> bar = foo;</span><br><span class="line">bar.a.b = <span class="number">2</span>;</span><br><span class="line"><span class="built_in">console</span>.log(foo.a.b);  <span class="comment">// 打印 2</span></span><br><span class="line"><span class="built_in">console</span>.log(foo === bar); <span class="comment">// 打印 true</span></span><br></pre></td></tr></table></figure>
<p>这就是深拷贝和浅拷贝。没有进行深拷贝，这样修改会导致值会跟着变化，前后两个值在判断的时候会被问题是同一个对象。</p>
<h3 id="合理使用Container和Dump"><a href="#合理使用Container和Dump" class="headerlink" title="合理使用Container和Dump"></a>合理使用Container和Dump</h3><p>有些组件的数据都是从父组件一直传递到子组件，这样当父组件渲染的时候，子组件也会跟着渲染。所以比如list，dialog等类型的组件应该是在redux里面，然后container进行connect。只有这些变化的时候才会重新render，否则父组件重新render也不会让子组件重新render因为，子组件的props都没有变化。<br>定制shouldComponentUpdate函数<br>shouldComponentUpdate(nextProps,nextState) false不render，true才render。如果啥也不反悔默认返回true。在最新的react中，react给我们提供了React.PureComponent，官方也在早期提供了名为react-addons-pure-render-mixin插件来重新实现shouldComponentUpdate生命周期方法。<br>immutable与with-immutable-props-to-js<br>建议使用seamless-immutable。javascript中的对象一般都是可变的，因为使用了引用赋值，新的对象简单的引用了原始对象，改变新对象将影响到原始对象。这样做非常的昂贵，对cpu和内存会造成浪费。可以用withImmutablePropsToJs再包一层，保证在组件拿到的props后是同一个对象，只要对比值就可以了。</p>
<h3 id="针对React的diff算法加入key，防止最坏情况的发生"><a href="#针对React的diff算法加入key，防止最坏情况的发生" class="headerlink" title="针对React的diff算法加入key，防止最坏情况的发生"></a>针对React的diff算法加入key，防止最坏情况的发生</h3><p>react为了追求高性能，采用了时间复杂度为O(N)来比较两个属性结构的区别。传统的比较两个树形结构，需要通过O(N^3)，这样性能很低。</p>
<p>但是最强高性能的代价就是要付出高开销。例如以下的变动react在对比的时候发现Text1和Text2不是同一个组件然后就直接销毁了，再重新生成，但是事实上他们只是换了一下顺序而已。这样一来，diff算法是很快，但是问题是消耗也很高啊。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 例1</span></span><br><span class="line"><span class="comment">// 原来的结点</span></span><br><span class="line">&lt;div&gt;</span><br><span class="line">  &lt;Text1 /&gt;</span><br><span class="line">  &lt;Text2 /&gt;</span><br><span class="line">&lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ 变化后的结点</span></span><br><span class="line"><span class="regexp">&lt;div&gt;</span></span><br><span class="line"><span class="regexp">  &lt;Text2 /</span>&gt;</span><br><span class="line">  &lt;Text1 /&gt;</span><br><span class="line">&lt;<span class="regexp">/div&gt;</span></span><br></pre></td></tr></table></figure>
<p>两个节点一样，只是顺序不一样，导致了最坏时间复杂度。这种情况要避免其实很简单，就是加入唯一key，这样react就会根据key的变化，而不是根据顺序进行diff计算了。<br>还是刚才这个例子，加上key后。当顺序变化后，react发现只是key顺序变了，那就把两个结点的顺序换一下。而不是销毁然后重新绘制，性能就会高很多。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 例2</span></span><br><span class="line"><span class="comment">// 原来的结点</span></span><br><span class="line">&lt;div&gt;</span><br><span class="line">  &lt;Text1 key=<span class="string">'text1'</span>/&gt;</span><br><span class="line">  &lt;Text2 key=<span class="string">'text2'</span>/&gt;</span><br><span class="line">&lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ 变化后的结点</span></span><br><span class="line"><span class="regexp">&lt;div&gt;</span></span><br><span class="line"><span class="regexp">  &lt;Text2 key='text2'/</span>&gt;</span><br><span class="line">  &lt;Text1 key=<span class="string">'text1'</span>/&gt;</span><br><span class="line">&lt;<span class="regexp">/div&gt;</span></span><br></pre></td></tr></table></figure>
<p>但是要注意：设置key不能用index，举一个例子。<br>注意：以下例子是反例，是要用来说明几个正确的结论</p>
<ul>
<li>列表最好要加key</li>
<li>但是，不能用index作为key</li>
<li>最好使用id作为key</li>
</ul>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Example2</span> <span class="keyword">extends</span> <span class="title">Component</span></span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(props)&#123;</span><br><span class="line">  <span class="keyword">super</span>(props)</span><br><span class="line">  <span class="keyword">this</span>.state=&#123;</span><br><span class="line">   data:[<span class="string">'a'</span>,<span class="string">'b'</span>,<span class="string">'c'</span>,<span class="string">'d'</span>]</span><br><span class="line">  &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  handleChange = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">this</span>.setState(&#123;</span><br><span class="line">      data:[<span class="string">'d'</span>,<span class="string">'c'</span>,<span class="string">'b'</span>,<span class="string">'a'</span>]</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">  render()&#123;</span><br><span class="line">     <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">      &#123;</span></span><br><span class="line"><span class="xml">       this.state.data.map((val,idx)=&gt;<span class="tag">&lt;<span class="name">Item</span> <span class="attr">key</span>=<span class="string">&#123;idx&#125;</span> <span class="attr">parentValue</span>=<span class="string">&#123;val&#125;</span> /&gt;</span>)</span></span><br><span class="line"><span class="xml">      &#125;</span></span><br><span class="line"><span class="xml">      <span class="tag">&lt;<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">&#123;this.handleChange&#125;</span>&gt;</span>改变state<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span></span><br><span class="line"><span class="xml">     <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Item</span> <span class="keyword">extends</span> <span class="title">Component</span></span>&#123;</span><br><span class="line">    onChange = <span class="function">(<span class="params">e</span>) =&gt;</span> &#123;</span><br><span class="line">     <span class="keyword">this</span>.setState(&#123;</span><br><span class="line">      itemValue: e.target.value</span><br><span class="line">     &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">    render()&#123;</span><br><span class="line">     <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">li</span>&gt;</span>&#123;this.props.parentValue&#125;</span></span><br><span class="line"><span class="xml">      <span class="tag">&lt;<span class="name">Input</span> <span class="attr">itemValue</span>=<span class="string">&#123;this.state.itemValue&#125;</span> <span class="attr">onChange</span>=<span class="string">&#123;this.onChange&#125;/</span>&gt;</span></span></span><br><span class="line"><span class="xml">     <span class="tag">&lt;/<span class="name">li</span>&gt;</span></span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Input</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line"> render()&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">value</span>=<span class="string">&#123;this.props.itemValue&#125;</span> <span class="attr">onChange</span>=<span class="string">&#123;this.props.onChange&#125;/</span>&gt;</span></span></span><br><span class="line"> &#125; </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Text1</span> <span class="keyword">extends</span> <span class="title">Component</span></span>&#123;</span><br><span class="line"> render()&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">this</span>.props.value ||<span class="string">''</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Text2</span> <span class="keyword">extends</span> <span class="title">Component</span></span>&#123;</span><br><span class="line"> render()&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">this</span>.props.value ||<span class="string">''</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>react中对组件的几个定义：ReactEmptyComponent空组件，ReactTextComponent文本组件，ReactDOMComponent浏览器组件，ReactCompositeComponent自定义组件。</p>
<p>上面实例中在数组重新排序后，key对应的实例都没有销毁，而是重新更新。具体更新过程我们拿key=0的元素来说明， 数组重新排序后：</p>
<p>首先，state改变肯定会引起重新render，组件重新render后得到新的虚拟dom。新老两个虚拟dom进行diff，新老版的都有key=0的组件，react认为同一个组件，则只可能更新组件；</p>
<p>然后，比较其children，子组件有两个，一个是文本组件ReactTextComponent，另一个是浏览器的input组件ReactDOMComponent；文本组件是受控组件，值变化了，重新render，重新赋值。input组件是非受控组件，由于父组件重新render了所以自己也重新render，但是并没有重新赋值。</p>
<p>这里有几个重要结论：</p>
<ul>
<li>key=0的组件并没有销毁，而是进行了diff比较；</li>
<li>input是非受控组件，可以重新render但是它的值并没有改变；</li>
<li>重新render和重新赋值是两个概念；</li>
<li>受控和非受控最好不好互相转换；</li>
<li>把受控组件转成非受控组件，会导致受控在某处就断开了；</li>
<li>把非受控组件转成非空组件，并且在willReceive判断进行setState以此来达到受控的目的，会导致有时候外部一个改变直接导致了非受控组件的变化，把自己的值都清空了；</li>
</ul>
<p>最重要的一个结论：<br>比如例1，如果没有加key，会导致<code>&lt;Text1 /&gt;</code>和<code>&lt;Text2 /&gt;</code>先销毁然后再重新生成。这样会很消耗性能。<br>而例2，react会知道不用销毁，只要调整一下顺序即可，也就是只要重新render执行更新的生命周期就行了，这样就减少了不必要的性能浪费。</p>
<h4 id="尽量减少新建变量和bind函数，传递参数是尽量减少传递参数的数量"><a href="#尽量减少新建变量和bind函数，传递参数是尽量减少传递参数的数量" class="headerlink" title="尽量减少新建变量和bind函数，传递参数是尽量减少传递参数的数量"></a>尽量减少新建变量和bind函数，传递参数是尽量减少传递参数的数量</h4><p>最好的做法。<code>onClick={this.handleClick}</code>构造函数每一次渲染的时候只会执行一遍；这种方法最好。这种写法初学者经常会遇到的一个问题就是undefined，这是因为没有使用箭头函数。普通函数中，this指向其函数的直接调用者；箭头函数中，this指向其定义环境，任何方法都改变不了其指向，如call（）、bind（）等；构造函数中，如果不使用new,则this指向window，如果使用new创建了一个实例，则this指向该实例。<br>中等做法。<code>onClick={this.handleClick.bind(this)}</code>在每次render()的时候都会重新执行一遍函数。<br>最差的做法。<code>onClick={()=&gt;{this.handleClick()}}</code>每一次render()的时候，都会生成一个新的匿名函数，即使两个箭头函数的内容是一样的。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// A</span></span><br><span class="line">&lt;ul&gt;</span><br><span class="line">  &lt;TodoItem text=<span class="string">"First"</span> complete=&#123;<span class="literal">false</span>&#125; /&gt;</span><br><span class="line">  &lt;TodoItem text=<span class="string">"Second"</span> complete=&#123;<span class="literal">false</span>&#125; /&gt;</span><br><span class="line">&lt;<span class="regexp">/ul&gt;</span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ B</span></span><br><span class="line"><span class="regexp">&lt;ul&gt;</span></span><br><span class="line"><span class="regexp">  &lt;TodoItem text="Zero" complete=&#123;false&#125; /</span>&gt;</span><br><span class="line">  &lt;TodoItem text=<span class="string">"First"</span> complete=&#123;<span class="literal">false</span>&#125; /&gt;</span><br><span class="line">  &lt;TodoItem text=<span class="string">"Second"</span> complete=&#123;<span class="literal">false</span>&#125; /&gt;</span><br><span class="line">&lt;<span class="regexp">/ul&gt;</span></span><br></pre></td></tr></table></figure>
<h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><h3 id="API"><a href="#API" class="headerlink" title="API"></a>API</h3><h4 id="React-Suspense"><a href="#React-Suspense" class="headerlink" title="React.Suspense"></a>React.Suspense</h4><p>React.Suspense 可以指定加载指示器（loading indicator），以防其组件树中的某些子组件尚未具备渲染条件。目前，懒加载组件是 &lt;React.Suspense&gt; 支持的唯一用例：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 该组件是动态加载的</span></span><br><span class="line"><span class="keyword">const</span> OtherComponent = React.lazy(<span class="function"><span class="params">()</span> =&gt;</span> <span class="keyword">import</span>(<span class="string">'./OtherComponent'</span>));</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">MyComponent</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="comment">// 显示 &lt;Spinner&gt; 组件直至 OtherComponent 加载完成</span></span><br><span class="line">    &lt;React.Suspense fallback=&#123;&lt;Spinner /&gt;&#125;&gt;</span><br><span class="line">      &lt;div&gt;</span><br><span class="line">        &lt;OtherComponent /&gt;</span><br><span class="line">      &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">    &lt;/</span>React.Suspense&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>lazy</code> 组件可以位于 <code>Suspense</code> 组件树的深处——它不必包装树中的每一个延迟加载组件。最佳实践是将 <code>&lt;Suspense&gt;</code> 置于你想展示加载指示器（loading indicator）的位置，而 <code>lazy()</code> 则可被放置于任何你想要做代码分割的地方。</p>
<h4 id="createPortal"><a href="#createPortal" class="headerlink" title="createPortal()"></a>createPortal()</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ReactDOM.createPortal(child, container)</span><br></pre></td></tr></table></figure>
<p>创建 portal。Portal 将提供一种将子节点渲染到 DOM 节点中的方式，该节点存在于 DOM 组件的层次结构之外。</p>
<h3 id="变换（Transformation）"><a href="#变换（Transformation）" class="headerlink" title="变换（Transformation）"></a>变换（Transformation）</h3><p>设计 React 的核心前提是认为 UI 只是把数据通过映射关系变换成另一种形式的数据。同样的输入必会有同样的输出。这恰好就是纯函数。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">NameBox</span>(<span class="params">name</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123; <span class="attr">fontWeight</span>: <span class="string">'bold'</span>, <span class="attr">labelContent</span>: name &#125;;</span><br><span class="line">&#125;</span><br><span class="line"><span class="string">'Sebastian Markbåge'</span> -&gt;</span><br><span class="line">&#123; <span class="attr">fontWeight</span>: <span class="string">'bold'</span>, <span class="attr">labelContent</span>: <span class="string">'Sebastian Markbåge'</span> &#125;;</span><br></pre></td></tr></table></figure>
<h3 id="抽象（Abstraction）"><a href="#抽象（Abstraction）" class="headerlink" title="抽象（Abstraction）"></a>抽象（Abstraction）</h3><p>你不可能仅用一个函数就能实现复杂的 UI。重要的是，你需要把 UI 抽象成多个隐藏内部细节，又可复用的函数。通过在一个函数中调用另一个函数来实现复杂的 UI，这就是抽象。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">FancyUserBox</span>(<span class="params">user</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    borderStyle: <span class="string">'1px solid blue'</span>,</span><br><span class="line">    childContent: [</span><br><span class="line">      <span class="string">'Name: '</span>,</span><br><span class="line">      NameBox(user.firstName + <span class="string">' '</span> + user.lastName)</span><br><span class="line">    ]</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line">&#123; <span class="attr">firstName</span>: <span class="string">'Sebastian'</span>, <span class="attr">lastName</span>: <span class="string">'Markbåge'</span> &#125; -&gt;</span><br><span class="line">&#123;</span><br><span class="line">  borderStyle: <span class="string">'1px solid blue'</span>,</span><br><span class="line">  childContent: [</span><br><span class="line">    <span class="string">'Name: '</span>,</span><br><span class="line">    &#123; <span class="attr">fontWeight</span>: <span class="string">'bold'</span>, <span class="attr">labelContent</span>: <span class="string">'Sebastian Markbåge'</span> &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h3 id="组合（Composition）"><a href="#组合（Composition）" class="headerlink" title="组合（Composition）"></a>组合（Composition）</h3><p>为了真正达到重用的特性，只重用叶子然后每次都为他们创建一个新的容器是不够的。你还需要可以包含其他抽象的容器再次进行组合。我理解的“组合”就是将两个或者多个不同的抽象合并为一个。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">FancyBox</span>(<span class="params">children</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    borderStyle: <span class="string">'1px solid blue'</span>,</span><br><span class="line">    children: children</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">UserBox</span>(<span class="params">user</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> FancyBox([</span><br><span class="line">    <span class="string">'Name: '</span>,</span><br><span class="line">    NameBox(user.firstName + <span class="string">' '</span> + user.lastName)</span><br><span class="line">  ]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="状态（State）"><a href="#状态（State）" class="headerlink" title="状态（State）"></a>状态（State）</h3><p>UI 不单单是对服务器端或业务逻辑状态的复制。实际上还有很多状态是针对具体的渲染目标。举个例子，在一个 text field 中打字。它不一定要复制到其他页面或者你的手机设备。滚动位置这个状态是一个典型的你几乎不会复制到多个渲染目标的。</p>
<p>我们倾向于使用不可变的数据模型。我们把可以改变 state 的函数串联起来作为原点放置在顶层。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">FancyNameBox</span>(<span class="params">user, likes, onClick</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> FancyBox([</span><br><span class="line">    <span class="string">'Name: '</span>, NameBox(user.firstName + <span class="string">' '</span> + user.lastName),</span><br><span class="line">    <span class="string">'Likes: '</span>, LikeBox(likes),</span><br><span class="line">    LikeButton(onClick)</span><br><span class="line">  ]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 实现细节</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> likes = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">addOneMoreLike</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  likes++;</span><br><span class="line">  rerender();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化</span></span><br><span class="line"></span><br><span class="line">FancyNameBox(</span><br><span class="line">  &#123; <span class="attr">firstName</span>: <span class="string">'Sebastian'</span>, <span class="attr">lastName</span>: <span class="string">'Markbåge'</span> &#125;,</span><br><span class="line">  likes,</span><br><span class="line">  addOneMoreLike</span><br><span class="line">);</span><br></pre></td></tr></table></figure>
<p>注意：本例更新状态时会带来副作用（addOneMoreLike 函数中）。我实际的想法是当一个“update”传入时我们返回下一个版本的状态，但那样会比较复杂。此示例待更新</p>
<h3 id="Memoization"><a href="#Memoization" class="headerlink" title="Memoization"></a>Memoization</h3><p>对于纯函数，使用相同的参数一次次调用未免太浪费资源。我们可以创建一个函数的 memorized 版本，用来追踪最后一个参数和结果。这样如果我们继续使用同样的值，就不需要反复执行它了。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">memoize</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> cachedArg;</span><br><span class="line">  <span class="keyword">var</span> cachedResult;</span><br><span class="line">  <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">arg</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (cachedArg === arg) &#123;</span><br><span class="line">      <span class="keyword">return</span> cachedResult;</span><br><span class="line">    &#125;</span><br><span class="line">    cachedArg = arg;</span><br><span class="line">    cachedResult = fn(arg);</span><br><span class="line">    <span class="keyword">return</span> cachedResult;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> MemoizedNameBox = memoize(NameBox);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">NameAndAgeBox</span>(<span class="params">user, currentTime</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> FancyBox([</span><br><span class="line">    <span class="string">'Name: '</span>,</span><br><span class="line">    MemoizedNameBox(user.firstName + <span class="string">' '</span> + user.lastName),</span><br><span class="line">    <span class="string">'Age in milliseconds: '</span>,</span><br><span class="line">    currentTime - user.dateOfBirth</span><br><span class="line">  ]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="列表（Lists）"><a href="#列表（Lists）" class="headerlink" title="列表（Lists）"></a>列表（Lists）</h3><p>大部分 UI 都是展示列表数据中不同 item 的列表结构。这是一个天然的层级。</p>
<p>为了管理列表中的每一个 item 的 state ，我们可以创造一个 Map 容纳具体 item 的 state。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">UserList</span>(<span class="params">users, likesPerUser, updateUserLikes</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> users.map(<span class="function"><span class="params">user</span> =&gt;</span> FancyNameBox(</span><br><span class="line">    user,</span><br><span class="line">    likesPerUser.get(user.id),</span><br><span class="line">    () =&gt; updateUserLikes(user.id, likesPerUser.get(user.id) + <span class="number">1</span>)</span><br><span class="line">  ));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> likesPerUser = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">updateUserLikes</span>(<span class="params">id, likeCount</span>) </span>&#123;</span><br><span class="line">  likesPerUser.set(id, likeCount);</span><br><span class="line">  rerender();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">UserList(data.users, likesPerUser, updateUserLikes);</span><br></pre></td></tr></table></figure>
<p>注意：现在我们向 FancyNameBox 传了多个不同的参数。这打破了我们的 memoization 因为我们每次只能存储一个值。更多相关内容在下面。</p>
<h3 id="连续性（Continuations）"><a href="#连续性（Continuations）" class="headerlink" title="连续性（Continuations）"></a>连续性（Continuations）</h3><p>不幸的是，自从 UI 中有太多的列表，明确的管理就需要大量的重复性样板代码。</p>
<p>我们可以通过推迟一些函数的执行，进而把一些模板移出业务逻辑。比如，使用“柯里化”（JavaScript 中的 bind，把多个参数转换成一个个接收单个参数的函数）。然后我们可以从核心的函数外面传递 state，这样就没有样板代码了。</p>
<p>下面这样并没有减少样板代码，但至少把它从关键业务逻辑中剥离。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">FancyUserList</span>(<span class="params">users</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> FancyBox(</span><br><span class="line">    UserList.bind(<span class="literal">null</span>, users)</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> box = FancyUserList(data.users);</span><br><span class="line"><span class="keyword">const</span> resolvedChildren = box.children(likesPerUser, updateUserLikes);</span><br><span class="line"><span class="keyword">const</span> resolvedBox = &#123;</span><br><span class="line">  ...box,</span><br><span class="line">  children: resolvedChildren</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h3 id="State-Map"><a href="#State-Map" class="headerlink" title="State Map"></a>State Map</h3><p>之前我们知道可以使用组合避免重复执行相同的东西这样一种重复模式。我们可以把执行和传递 state 逻辑挪动到被复用很多的低层级的函数中去。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">FancyBoxWithState</span>(<span class="params"></span></span></span><br><span class="line"><span class="function"><span class="params">  children,</span></span></span><br><span class="line"><span class="function"><span class="params">  stateMap,</span></span></span><br><span class="line"><span class="function"><span class="params">  updateState</span></span></span><br><span class="line"><span class="function"><span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> FancyBox(</span><br><span class="line">    children.map(<span class="function"><span class="params">child</span> =&gt;</span> child.continuation(</span><br><span class="line">      stateMap.get(child.key),</span><br><span class="line">      updateState</span><br><span class="line">    ))</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">UserList</span>(<span class="params">users</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> users.map(<span class="function"><span class="params">user</span> =&gt;</span> &#123;</span><br><span class="line">    continuation: FancyNameBox.bind(<span class="literal">null</span>, user),</span><br><span class="line">    key: user.id</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">FancyUserList</span>(<span class="params">users</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> FancyBoxWithState.bind(<span class="literal">null</span>,</span><br><span class="line">    UserList(users)</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> continuation = FancyUserList(data.users);</span><br><span class="line">continuation(likesPerUser, updateUserLikes);</span><br></pre></td></tr></table></figure>
<h3 id="Memoization-Map"><a href="#Memoization-Map" class="headerlink" title="Memoization Map"></a>Memoization Map</h3><p>一旦我们想在一个 memoization 列表中 memoize 多个 item 就会变得很困难。因为你需要制定复杂的缓存算法来平衡调用频率和内存占有率。</p>
<p>还好 UI 在同一个位置会相对的稳定。相同的位置一般每次都会接受相同的参数。这样以来，使用一个集合来做 memoization 是一个非常好用的策略。</p>
<p>我们可以用对待 state 同样的方式，在组合的函数中传递一个 memoization 缓存。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">memoize</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">arg, memoizationCache</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (memoizationCache.arg === arg) &#123;</span><br><span class="line">      <span class="keyword">return</span> memoizationCache.result;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">const</span> result = fn(arg);</span><br><span class="line">    memoizationCache.arg = arg;</span><br><span class="line">    memoizationCache.result = result;</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">FancyBoxWithState</span>(<span class="params"></span></span></span><br><span class="line"><span class="function"><span class="params">  children,</span></span></span><br><span class="line"><span class="function"><span class="params">  stateMap,</span></span></span><br><span class="line"><span class="function"><span class="params">  updateState,</span></span></span><br><span class="line"><span class="function"><span class="params">  memoizationCache</span></span></span><br><span class="line"><span class="function"><span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> FancyBox(</span><br><span class="line">    children.map(<span class="function"><span class="params">child</span> =&gt;</span> child.continuation(</span><br><span class="line">      stateMap.get(child.key),</span><br><span class="line">      updateState,</span><br><span class="line">      memoizationCache.get(child.key)</span><br><span class="line">    ))</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> MemoizedFancyNameBox = memoize(FancyNameBox);</span><br></pre></td></tr></table></figure>
<h3 id="代数效应（Algebraic-Effects）"><a href="#代数效应（Algebraic-Effects）" class="headerlink" title="代数效应（Algebraic Effects）"></a>代数效应（Algebraic Effects）</h3><p>多层抽象需要共享琐碎数据时，一层层传递数据非常麻烦。如果能有一种方式可以在多层抽象中快捷地传递数据，同时又不需要牵涉到中间层级，那该有多好。React 中我们把它叫做“context”。</p>
<p>有时候数据依赖并不是严格按照抽象树自上而下进行。举个例子，在布局算法中，你需要在实现他们的位置之前了解子节点的大小。</p>
<p>现在，这个例子有一点超纲。我会使用 代数效应 这个由我发起的 ECMAScript 新特性提议。如果你对函数式编程很熟悉，它们 在避免由 monad 强制引入的仪式一样的编码。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">ThemeBorderColorRequest</span>(<span class="params"></span>) </span>&#123; &#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">FancyBox</span>(<span class="params">children</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> color = raise <span class="keyword">new</span> ThemeBorderColorRequest();</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    borderWidth: <span class="string">'1px'</span>,</span><br><span class="line">    borderColor: color,</span><br><span class="line">    children: children</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">BlueTheme</span>(<span class="params">children</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">try</span> &#123;</span><br><span class="line">    children();</span><br><span class="line">  &#125; <span class="keyword">catch</span> effect ThemeBorderColorRequest -&gt; [, continuation] &#123;</span><br><span class="line">    continuation(<span class="string">'blue'</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">App</span>(<span class="params">data</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> BlueTheme(</span><br><span class="line">    FancyUserList.bind(<span class="literal">null</span>, data.users)</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="keys-amp-props"><a href="#keys-amp-props" class="headerlink" title="keys &amp; props"></a>keys &amp; props</h3><p>推荐 clone 我的源码<a href="https://github.com/wangruolong/web-front-end/tree/master/thinking-in-react" target="_blank" rel="noopener">https://github.com/wangruolong/web-front-end/tree/master/thinking-in-react</a><br>里面有大量的注释和精心制作的运行结果，可以助你快速掌握！</p>
<h4 id="发现问题"><a href="#发现问题" class="headerlink" title="发现问题"></a>发现问题</h4><p>我们经常需要对一个数组进行循环，渲染一个列表。但是控制台经常会报警告 Warning: Each child in a list should have a unique “key” prop.有没有思考过这是为什么？<br>遇到这种问题的一般做法是直接把 index 赋值给 key。这样会有什么问题？<br>重新 render 的时候出问题了。为什么会这样？</p>
<h4 id="分析问题"><a href="#分析问题" class="headerlink" title="分析问题"></a>分析问题</h4><p>不加 key 的情况下，react 分不清哪个是哪个。<br>React 在循环的时候自动帮元素加上序号。<br>diff 算法的比较规则，优先比较 key，如果 key 不存在则比较组件名称，如果组件名称一样则 react 认为是同一个组件，不 unmount 直接重新 render。至于 props 里面的数据一不一样，React 认为这只是 props 数据变化了而已。React diff 算法可以参考这篇文章 <a href="https://www.jianshu.com/p/3ba0822018cf" target="_blank" rel="noopener">https://www.jianshu.com/p/3ba0822018cf</a>）<br>不加 key 的情况下，react 会默认加上 key，当对比到同一个 key 的组件名称不一致，就直接销毁掉了。<br>加 key 后，react 会根据 key 进行比较，同一个 key 如果组件名称也一样，则认为是同一个组件不 unmount，而是重新 render。</p>
<h4 id="解决问题"><a href="#解决问题" class="headerlink" title="解决问题"></a>解决问题</h4><p>为什么 input 的值会没掉？<br>input 有没有重新 render？但是有没有被销毁？为什么？<br>因为 input 的值既不是受控也不是非受控，他的值并没有被 react 维护。而值被清空了并不是说组件被销毁了，而是说重新 render 了，但是值没有被保留下来。<br>非受控组件。this.state.value 并没有受到 props 的控制，所以当重新 render 后，input 还是由原来的值控制。<br>只不过 props 改变了，这个本质上和原来是同一个组件，只不过 props 变了而已。</p>
<h3 id="hooks"><a href="#hooks" class="headerlink" title="hooks"></a>hooks</h3><p>为了处理在触发的时候还不存在，等到执行的时候才存在的对象。</p>
<h4 id="发现问题-1"><a href="#发现问题-1" class="headerlink" title="发现问题"></a>发现问题</h4><p>场景：一个搜索组件 Search，一个文章详情组件 Article，搜索结果返回用 i 标签包裹的关键词。<br>需求：搜索文章关键词，点击结果文章自动滚动到第一个关键词。</p>
<h4 id="分析问题-1"><a href="#分析问题-1" class="headerlink" title="分析问题"></a>分析问题</h4><p>实现方式：在渲染结束后，真实 Dom 上面查找 i 节点，找到 i 节点后获取它的 offset 然后设置 Article 的 scrollTop。<br>这就要求必须要有真实 dom 的时候才搜索，所以要写在 didUpdate，写在 didMount 也可以但是它只会触发一次所以最终还是得写在 didUpdate。<br>didUpdate 会被很多种情况触发，常规的做法是在 didUpdate 里面设置各种属性判断，但是这样会导致项目越来越难维护。didUpdate 里面应该尽量简单。</p>
<h4 id="解决问题-1"><a href="#解决问题-1" class="headerlink" title="解决问题"></a>解决问题</h4><p>用钩子，可以在点击的那个地方挂上一个钩子，然后在 didUpdate 那边不断循环这些钩子，一旦钩子被执行就直接销毁，这样也不会有副作用。<br>相当于在点击的时候 producer，然后在执行的地方 consumer，执行之后把相应的钩子销毁掉就行了。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 钩子数组，我有很多把钩子。</span></span><br><span class="line"><span class="keyword">const</span> hooks = [];</span><br><span class="line"><span class="comment">// 一根钩子抹点油。怎么样？我就问你这种注释看得懂吗？</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">hook</span>(<span class="params">subscriber</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> subscriber !== <span class="string">"function"</span>) &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">"Invalid hook, must be a function!"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">const</span> subscriberIndex = hooks.indexOf(subscriber);</span><br><span class="line">  <span class="keyword">if</span> (subscriberIndex == <span class="number">-1</span>) &#123;</span><br><span class="line">    hooks.push(subscriber);</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> result = hooks.splice(hooks.indexOf(subscriber), <span class="number">1</span>);</span><br><span class="line">      <span class="keyword">return</span> result[<span class="number">0</span>];</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> result = hooks.splice(subscriberIndex, <span class="number">1</span>);</span><br><span class="line">      <span class="keyword">return</span> result[<span class="number">0</span>];</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 篮子</span></span><br><span class="line"><span class="keyword">const</span> baskets = &#123;&#125;;</span><br><span class="line"><span class="comment">// 出钩</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">goHook</span>(<span class="params">key, func</span>) </span>&#123;</span><br><span class="line">  baskets[key] = hook(func);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 收钩。把参数带上。</span></span><br><span class="line"><span class="comment">// 这个...args的作用是【rest】，接收剩余参数。</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">backHook</span>(<span class="params">key, ...args</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (baskets[key]) &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">"找到钩子"</span>, baskets[key]);</span><br><span class="line">      <span class="comment">// 这个...args的作用是【spread】，把对象展开。</span></span><br><span class="line">      baskets[key]()(...args);</span><br><span class="line">      <span class="keyword">delete</span> baskets[key];</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">"没找到钩子"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(e);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> &#123; hooks, baskets, goHook, backHook &#125;;</span><br></pre></td></tr></table></figure>
<p>react节点变化后会产生什么影响<br>http2多路复用</p>

      
    </div>

    

    
    
    

    

    

    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/blog/tags/React哲学/" rel="tag"># React哲学</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/blog/2022/05/20/blog/thinking-in-javascript/thinking-in-javascript/" rel="next" title="Thinking in Javascript">
                <i class="fa fa-chevron-left"></i> Thinking in Javascript
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/blog/2022/07/03/blog/super-engineer/super-engineer/" rel="prev" title="数字化时代的“超级工程师”">
                数字化时代的“超级工程师” <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>


  </div>


          </div>
          

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <p class="site-author-name" itemprop="name">对美好事物的向往，心中要充满强烈的愿望！</p>
              <p class="site-description motion-element" itemprop="description">沉淀下来的知识，才是真正的财富！</p>
          </div>

          
            <nav class="site-state motion-element">
              
                <div class="site-state-item site-state-posts">
                
                  <a href="/blog/archives/">
                
                    <span class="site-state-item-count">19</span>
                    <span class="site-state-item-name">日志</span>
                  </a>
                </div>
              

              
                
                
                <div class="site-state-item site-state-categories">
                  <a href="/blog/categories/index.html">
                    
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                    <span class="site-state-item-count">22</span>
                    <span class="site-state-item-name">分类</span>
                  </a>
                </div>
              

              
                
                
                <div class="site-state-item site-state-tags">
                  <a href="/blog/tags/index.html">
                    
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                    <span class="site-state-item-count">19</span>
                    <span class="site-state-item-name">标签</span>
                  </a>
                </div>
              
            </nav>
          

          

          

          
          

          
          

          
            
          
          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#生命周期"><span class="nav-number">1.</span> <span class="nav-text">生命周期</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#要掌握生命周期首先要理解Fiber"><span class="nav-number">1.1.</span> <span class="nav-text">要掌握生命周期首先要理解Fiber</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#同步更新过程的局限"><span class="nav-number">1.1.1.</span> <span class="nav-text">同步更新过程的局限</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#React-Fiber的方式"><span class="nav-number">1.1.2.</span> <span class="nav-text">React Fiber的方式</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#为什么叫Fiber呢？"><span class="nav-number">1.1.3.</span> <span class="nav-text">为什么叫Fiber呢？</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#React-Fiber对现有代码的影响"><span class="nav-number">1.1.4.</span> <span class="nav-text">React Fiber对现有代码的影响</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#旧版的生命周期（V15-6及以前）"><span class="nav-number">1.2.</span> <span class="nav-text">旧版的生命周期（V15.6及以前）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#新版的生命周期（V16-3）"><span class="nav-number">1.3.</span> <span class="nav-text">新版的生命周期（V16.3）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#新版的生命周期（V16-4）"><span class="nav-number">1.4.</span> <span class="nav-text">新版的生命周期（V16.4）</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#性能优化"><span class="nav-number">2.</span> <span class="nav-text">性能优化</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#虚拟化长列表"><span class="nav-number">2.1.</span> <span class="nav-text">虚拟化长列表</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#使用shouldComponentUpdate避免调停"><span class="nav-number">2.2.</span> <span class="nav-text">使用shouldComponentUpdate避免调停</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#合理使用Container和Dump"><span class="nav-number">2.3.</span> <span class="nav-text">合理使用Container和Dump</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#针对React的diff算法加入key，防止最坏情况的发生"><span class="nav-number">2.4.</span> <span class="nav-text">针对React的diff算法加入key，防止最坏情况的发生</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#尽量减少新建变量和bind函数，传递参数是尽量减少传递参数的数量"><span class="nav-number">2.4.1.</span> <span class="nav-text">尽量减少新建变量和bind函数，传递参数是尽量减少传递参数的数量</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#其他"><span class="nav-number">3.</span> <span class="nav-text">其他</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#API"><span class="nav-number">3.1.</span> <span class="nav-text">API</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#React-Suspense"><span class="nav-number">3.1.1.</span> <span class="nav-text">React.Suspense</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#createPortal"><span class="nav-number">3.1.2.</span> <span class="nav-text">createPortal()</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#变换（Transformation）"><span class="nav-number">3.2.</span> <span class="nav-text">变换（Transformation）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#抽象（Abstraction）"><span class="nav-number">3.3.</span> <span class="nav-text">抽象（Abstraction）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#组合（Composition）"><span class="nav-number">3.4.</span> <span class="nav-text">组合（Composition）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#状态（State）"><span class="nav-number">3.5.</span> <span class="nav-text">状态（State）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Memoization"><span class="nav-number">3.6.</span> <span class="nav-text">Memoization</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#列表（Lists）"><span class="nav-number">3.7.</span> <span class="nav-text">列表（Lists）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#连续性（Continuations）"><span class="nav-number">3.8.</span> <span class="nav-text">连续性（Continuations）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#State-Map"><span class="nav-number">3.9.</span> <span class="nav-text">State Map</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Memoization-Map"><span class="nav-number">3.10.</span> <span class="nav-text">Memoization Map</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#代数效应（Algebraic-Effects）"><span class="nav-number">3.11.</span> <span class="nav-text">代数效应（Algebraic Effects）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#keys-amp-props"><span class="nav-number">3.12.</span> <span class="nav-text">keys &amp; props</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#发现问题"><span class="nav-number">3.12.1.</span> <span class="nav-text">发现问题</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#分析问题"><span class="nav-number">3.12.2.</span> <span class="nav-text">分析问题</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#解决问题"><span class="nav-number">3.12.3.</span> <span class="nav-text">解决问题</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#hooks"><span class="nav-number">3.13.</span> <span class="nav-text">hooks</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#发现问题-1"><span class="nav-number">3.13.1.</span> <span class="nav-text">发现问题</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#分析问题-1"><span class="nav-number">3.13.2.</span> <span class="nav-text">分析问题</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#解决问题-1"><span class="nav-number">3.13.3.</span> <span class="nav-text">解决问题</span></a></li></ol></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright"> &copy; <span itemprop="copyrightYear">2024</span>
  <span class="with-love" id="animate">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">对美好事物的向往，心中要充满强烈的愿望！</span>

  

  
</div>




  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动 v3.9.0</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 – <a class="theme-link" target="_blank" href="https://theme-next.org">NexT.Gemini</a> v6.4.2</div>




        








        
        <div>
          <a href="https://beian.miit.gov.cn/" target="_blank">闽ICP备15015722号-2</a>
        </div>
      </div>
     
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    
	
    

    
  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>


























  
  
    <script type="text/javascript" src="/blog/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  


  


  <script type="text/javascript" src="/blog/js/src/utils.js?v=6.4.2"></script>

  <script type="text/javascript" src="/blog/js/src/motion.js?v=6.4.2"></script>



  
  


  <script type="text/javascript" src="/blog/js/src/affix.js?v=6.4.2"></script>

  <script type="text/javascript" src="/blog/js/src/schemes/pisces.js?v=6.4.2"></script>



  
  <script type="text/javascript" src="/blog/js/src/scrollspy.js?v=6.4.2"></script>
<script type="text/javascript" src="/blog/js/src/post-details.js?v=6.4.2"></script>



  


  <script type="text/javascript" src="/blog/js/src/bootstrap.js?v=6.4.2"></script>



  



  










  





  

  

  

  

  

  
  

  

  

  

  

  

</body>
</html>
